// 图片压缩封装方法
export const dataURLtoFile = (dataurl: any, filename: any) => { // 将base64转换为file文件
  const arr = dataurl.split(',')
  const mime = arr[0].match(/:(.*?);/)[1]
  const bstr = atob(arr[1])
  let n = bstr.length
  const u8arr = new Uint8Array(n)
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n)
  }
  return new File([u8arr], filename, { type: mime })
}
// canvas生成的格式为base64，需要进行转化, base64->blob
export const dataURLtoBlob = (dataurl: any) => {
  const arr = dataurl.split(',')
  const mime = arr[0].match(/:(.*?);/)[1]
  const bstr = atob(arr[1])
  let n = bstr.length
  const u8arr = new Uint8Array(n)
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n)
  }
  return new Blob([u8arr], { type: mime })
}
// 压缩图片
export const imgZip = (file: any) => {
  // var _this = this
  const imgZipStatus = new Promise((resolve, reject) => {
    const reader: FileReader = new FileReader()
    // readAsDataURL 方法会读取指定的 Blob 或 File 对象。读取操作完成的时候，readyState 会变成已完成DONE，并触发 loadend (en-US) 事件，
    // 同时 result 属性将包含一个data:URL格式的字符串（base64编码）以表示所读取文件的内容。
    reader.readAsDataURL(file)
    reader.onload = () => {
      const img: HTMLImageElement = new Image()
      img.src = reader.result
      img.onload = () => {
        // 图片的宽高
        const w = img.width
        const h = img.height
        const canvas = document.createElement('canvas')
        // canvas对图片进行裁剪，这里设置为图片的原始尺寸
        canvas.width = w
        canvas.height = h
        const ctx: CanvasRenderingContext2D | null = canvas.getContext('2d')
        // canvas中，png转jpg会变黑底，所以先给canvas铺一张白底
        ctx!.fillStyle = '#fff'
        // fillRect()方法绘制一个填充了内容的矩形，这个矩形的开始点（左上点）在
        // (x, y) ，它的宽度和高度分别由width 和 height 确定，填充样式由当前的fillStyle 决定。
        ctx!.fillRect(0, 0, canvas.width, canvas.height)
        // 绘制图像
        ctx!.drawImage(img, 0, 0, w, h)

        // canvas转图片达到图片压缩效果
        // 返回一个包含图片展示的 data URI base64 在指定图片格式为 image/jpeg 或 image/webp的情况下，
        // 可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围，将会使用默认值 0.92。其他参数会被忽略。
        const fileBase64 = canvas.toDataURL('image/jpeg', 0.8)
        // base64格式文件转成Blob文件格式
        const blobFile = dataURLtoBlob(fileBase64)
        // // console.log('压缩后的图片：Blob文件----------')
        console.log(blobFile)
        // base64格式文件转成file文件格式
        const fileName = file.name
        const fileImg = dataURLtoFile(fileBase64, fileName)
        // console.log('压缩后的图片：file文件----------')
        // console.log(fileImg)
        resolve({ fileImg, fileBase64 })
      }
    }
  })
  return imgZipStatus
}
// 压缩图片到指定大小
// 将File（Blob）对象转变为一个dataURL字符串， 即base64格式
const fileToDataURL = (file: any) => new Promise((resolve) => {
  const reader = new FileReader()
  reader.onloadend = e => resolve(e.target.result)
  reader.readAsDataURL(file)
})

// 将dataURL字符串转变为image对象，即base64转img对象
const dataURLToImage = (dataURL: any) => new Promise((resolve) => {
  const img = new Image()
  img.onload = () => resolve(img)
  img.src = dataURL
})

// 将一个canvas对象转变为一个File（Blob）对象
const canvastoFile = (canvas: HTMLCanvasElement, type: string, quality: number) => new Promise(resolve => canvas.toBlob(blob => resolve(blob), type, quality))

// eslint-disable-next-line no-async-promise-executor
export const compress = (originfile: { size: number; name: string }, maxSize: number) => new Promise(async (resolve, reject) => {
  const originSize = originfile.size / 1024 // 单位为kb
  // console.log('图片指定最大尺寸为', maxSize, '原始尺寸为：', originSize)
  // 将原图片转换成base64
  const base64 = await fileToDataURL(originfile)

  // 缩放图片需要的canvas
  const canvas: HTMLCanvasElement = document.createElement('canvas')
  const context: CanvasRenderingContext2D | null = canvas.getContext('2d')

  // 小于maxSize，则不需要压缩，直接返回
  if (originSize < maxSize) {
    resolve({ compressBase64: base64, compressFile: originfile })
    // console.log(`图片小于指定大小:${maxSize}KB，不用压缩`)
    return
  }

  const img: any = await dataURLToImage(base64)

  const scale = 1
  const originWidth = img.width
  const originHeight = img.height
  const targetWidth = originWidth * scale
  const targetHeight = originHeight * scale

  canvas.width = targetWidth
  canvas.height = targetHeight
  // 垂直镜像：y轴翻转
  // context.translate(0, canvas.height)
  // context.scale(1, -1)

  // 垂直镜像：x轴翻转
  // context.translate(canvas.width, 0)
  // context.scale(-1, 1)
  context!.clearRect(0, 0, targetWidth, targetHeight)
  context!.drawImage(img, 0, 0, targetWidth, targetHeight)

  // 将Canvas对象转变为dataURL字符串，即压缩后图片的base64格式
  // const compressedBase64 = canvas.toDataURL('image/jpeg', 0.1);
  // 经过我的对比，通过scale控制图片的拉伸来压缩图片，能够压缩jpg，png等格式的图片
  // 通过canvastoFile方法传递quality来压缩图片，只能压缩jpeg类型的图片，png等格式不支持
  // scale的压缩效果没有canvastoFile好
  // 在压缩到指定大小时，通过scale压缩的图片比通过quality压缩的图片模糊的多
  // 压缩的思路，用二分法找最佳的压缩点
  // 这里为了规避浮点数计算的弊端，将quality转为整数再计算;
  // const preQuality = 100;
  const maxQualitySize: {
    quality: number; size: number;
  } = { quality: 100, size: Number.MAX_SAFE_INTEGER }
  const minQualitySize: { quality: number, size: number } = { quality: 0, size: 0 }
  let quality: number = 100
  let count: number = 0 // 压缩次数
  let compressFinish: boolean = false // 压缩完成
  let invalidDesc: string = ''
  let compressBlob: any = null

  // 二分法最多尝试8次即可覆盖全部可能
  while (!compressFinish && count < 12) {
    compressBlob = await canvastoFile(canvas, 'image/jpeg', quality / 100)
    const compressSize = compressBlob.size / 1024
    count++
    if (compressSize === maxSize) {
      // console.log(`压缩完成，总共压缩了${count}次`)
      compressFinish = true
      return
    }
    if (compressSize > maxSize) {
      maxQualitySize.quality = quality
      maxQualitySize.size = compressSize
    }
    if (compressSize < maxSize) {
      minQualitySize.quality = quality
      minQualitySize.size = compressSize
    }
    // console.log(`第${count}次压缩,压缩后大小${compressSize},quality参数:${quality}`)

    quality = Math.ceil((maxQualitySize.quality + minQualitySize.quality) / 2)

    if (maxQualitySize.quality - minQualitySize.quality < 2) {
      if (!minQualitySize.size && quality) {
        quality = minQualitySize.quality
      } else if (!minQualitySize.size && !quality) {
        compressFinish = true
        invalidDesc = '压缩失败，无法压缩到指定大小'
        // console.log(`压缩完成，总共压缩了${count}次`)
      } else if (minQualitySize.size > maxSize) {
        compressFinish = true
        invalidDesc = '压缩失败，无法压缩到指定大小'
        // console.log(`压缩完成，总共压缩了${count}次`)
      } else {
        // console.log(`压缩完成，总共压缩了${count}次`)
        compressFinish = true
        quality = minQualitySize.quality
      }
    }
  }

  if (invalidDesc) {
    // 压缩失败，则返回原始图片的信息
    console.log(`压缩失败，无法压缩到指定大小：${maxSize}KB`)
    // eslint-disable-next-line prefer-promise-reject-errors
    reject({ msg: invalidDesc, compressBase64: base64, compressFile: originfile })
    return
  }

  compressBlob = await canvastoFile(canvas, 'image/jpeg', quality / 100)
  const compressSize = compressBlob.size / 1024
  // console.log(`最后一次压缩（即第${count + 1}次），quality为:${quality}，大小：${compressSize}`)
  const compressedBase64 = await fileToDataURL(compressBlob)

  const compressedFile = new File([compressBlob], originfile.name, { type: 'image/jpeg' })
  resolve({ compressFile: compressedFile, compressBase64: compressedBase64 })
})